import math
import os

from graphics.Shape import Line, Polygon, Rectangle, Shape
from graphics.TextBox import TextBox

"""
    开始/结束符号[√]
中文名：开始/结束
英文名：Start/End
符号形状：跑道型

    处理/过程符号[√]
中文名：处理/过程
英文名：Process
符号形状：矩形（Rectangle）

    判断符号[√]
中文名：判断
英文名：Decision
符号形状：菱形（Diamond）

    输入/输出符号[√]
中文名：输入/输出
英文名：Input/Output
符号形状：平行四边形（Parallelogram）

    流线符号
中文名：流线
英文名：Flow Line
符号形状：带箭头的直线（Arrow Line）

    连接符号
中文名：连接
英文名：Connector
符号形状：圆形（Circle）

文件符号
中文名：文件
英文名：Document
符号形状：矩形加波浪线（Rectangle with wavy bottom）

注释符号
中文名：注释
英文名：Annotation/Comment
符号形状：虚线连接的矩形（Dashed-line Rectangle）
"""

class Arrow(Line):
    def __init__(self, arrow_size=10, arrow_angle=30,  ** kwargs):
        """
        :param arrow_size: 箭头三角形边长
        :param arrow_angle: 箭头张开角度（单位：度）
        """
        super().__init__(**kwargs)
        self.arrow_size = arrow_size
        self.arrow_angle = math.radians(arrow_angle)  # 转换为弧度

    def draw(self, canvas):
        # 先绘制父类的直线部分
        super().draw(canvas)

        # 计算变换后的端点坐标
        p0 = self._apply_transform(self.start)
        p1 = self._apply_transform(self.end)

        # 计算箭头方向向量
        dx = p1[0] - p0[0]
        dy = p1[1] - p0[1]
        angle = math.atan2(dy, dx)  # 线段方向角

        # 计算箭头顶点坐标（网页6几何算法）
        arrow_length = self.arrow_size * self.transform_matrix[0]  # 考虑X轴缩放
        # 修改箭头顶点计算部分
        left_point = (
            int(round(p1[0] - arrow_length * math.cos(angle + self.arrow_angle / 2))),
            int(round(p1[1] - arrow_length * math.sin(angle + self.arrow_angle / 2)))
        )
        right_point = (
            int(round(p1[0] - arrow_length * math.cos(angle - self.arrow_angle / 2))),
            int(round(p1[1] - arrow_length * math.sin(angle - self.arrow_angle / 2)))
        )

        # 绘制箭头三角形（网页3绘图规范）
        if self.line_width <= 2:
            # 使用中点算法绘制箭头边
            self.midpoint_line(canvas, p1[0], p1[1], left_point[0], left_point[1],
                               fill=self.color, width=self.line_width)
            self.midpoint_line(canvas, p1[0], p1[1], right_point[0], right_point[1],
                               fill=self.color, width=self.line_width)
        else:
            # 使用Tkinter原生方法绘制填充箭头
            canvas.create_polygon(
                p1[0], p1[1],
                left_point[0], left_point[1],
                right_point[0], right_point[1],
                fill=self.get_fill_color() if self.auto_fill else "",
                outline=self.color,
                width=self.line_width
            )

    def to_dict(self):
        data = super().to_dict()
        data.update({
            "type": "Arrow",  # 覆盖父类类型标识
            "arrow_size": self.arrow_size,
            "arrow_angle": math.degrees(self.arrow_angle)  # 存储为角度
        })
        return data

    @classmethod
    def from_dict(cls, data):
        return cls(
            arrow_size=data.get("arrow_size", 10),
            arrow_angle=data.get("arrow_angle", 30),
            start_x=data["start"][0],
            start_y=data["start"][1],
            end_x=data["end"][0],
            end_y=data["end"][1],
            color=data["color"],
            line_width=data["line_width"],
            transform=data["transform"],
            fill=data.get("fill"),
            auto_fill=data.get("auto_fill", False)
        )

    # 保留父类的移动和变换方法
    def move(self, dx, dy):
        """继承父类坐标移动逻辑"""
        super().move(dx, dy)

    def _apply_transform(self, point):
        """继承父类仿射变换处理"""
        return super()._apply_transform(point)

class Process(Rectangle,TextBox):
    def __init__(self, create_pos, points, x, y, font_size, color, line_width, **kwargs):
        # print(f"Process.__mro__:{Process.__mro__}")
        self.create_pos = create_pos
        kwargs.update({
            "points": points,
            "x": x,
            "y": y,
            "font_size": font_size,
            "color": color,
            "line_width": line_width
        })
        super().__init__(**kwargs)  # 通过MRO自动分配参数

    def draw(self, canvas):
        Rectangle.draw(self, canvas)
        TextBox.draw(self, canvas)

    def to_dict(self):
        # print("运行了一次")
        data = {
            "type": self.__class__.__name__,
            "is_filled": self.is_filled,
            "color": self.color,
            "line_width": self.line_width,
            "fill": self.fill,
            "auto_fill": self.auto_fill,

            "version": "2.0",  # 新增版本标识
            "points": self.points,

            "text": self.text,
            "x": self.x,
            "y": self.y,
            "font_size": self.font_size,
            "font_family": self.font_family,

            "create_pos":self.create_pos
        }
        print(f"data = {data}")
        return data
    @classmethod
    def from_dict(cls, data):
        # version = data.get("version", "1.0")
        return cls(
            points=[data["points"][0], data["points"][1]],
            is_filled=data.get("is_filled", data.get("auto_fill", False)),  # 兼容旧版字段
            color=data["color"],
            line_width=data["line_width"],
            transform=data.get("transform"),
            fill=data.get("fill"),
            auto_fill=data.get("auto_fill", False),

            x=data["x"],
            y=data["y"],
            text=data["text"],
            font_size=data["font_size"],
            font_family=data["font_family"],

            create_pos=data["create_pos"]
        )

"""Start/End"""
class StartOrEnd(Polygon,TextBox):
    def __init__(self, points, x, y, font_size, color, line_width,  ** kwargs):
        # print(f"Decision.__mro__:{StartOrEnd.__mro__}")
        kwargs.update({
            "points": points,
            "polygon_type": "generate_track",
            "x": x,
            "y": y,
            "font_size": font_size,
            "color": color,
            "line_width": line_width
        })
        super().__init__(**kwargs)  # 通过MRO自动分配参数


    def draw(self,canvas):
        Polygon.draw(self,canvas)
        TextBox.draw(self,canvas)



    def to_dict(self):
        # print("运行了一次")
        data = {
            "type": self.__class__.__name__,
            "is_filled": self.is_filled,
            "color": self.color,
            "line_width": self.line_width,
            "fill": self.fill,
            "auto_fill": self.auto_fill,

            "version": "2.0",  # 新增版本标识
            "points": [[round(x, 4), round(y, 4)] for x, y in self.points],  # 精度优化
            "closed": True,  # 标识多边形闭合特性
            "transform": [round(v, 6) for v in self.transform_matrix],

            "text": self.text,
            "x": self.x,
            "y": self.y,
            "font_size": self.font_size,
            "font_family": self.font_family,
        }
        print(f"data = {data}")
        return data

    @classmethod
    def from_dict(cls, data):
        # version = data.get("version", "1.0")
        return cls(
            points=data["points"],
            is_filled=data.get("is_filled", data.get("auto_fill", False)),  # 兼容旧版字段
            color=data["color"],
            line_width=data["line_width"],
            transform=data.get("transform", [1, 0, 0, 1, 0, 0]),  # 默认单位矩阵
            fill=data.get("fill", data["color"]),  # 填充色回退逻辑
            auto_fill=data.get("auto_fill", False),
            x=data["x"],
            y=data["y"],
            text=data["text"],
            font_size=data["font_size"],
            font_family=data["font_family"],
        )
class Decision(Polygon,TextBox):
    def __init__(self, points, x, y, font_size, color, line_width, ** kwargs):
        print(f"Decision.__mro__:{Decision.__mro__}")
        kwargs.update({
            "points": points,
            "polygon_type": "Diamond",
            "x": x,
            "y": y,
            "font_size": font_size,
            "color": color,
            "line_width": line_width
        })
        super().__init__(**kwargs)  # 通过MRO自动分配参数

        # 预期输出：Decision → Polygon → TextBox → Shape → object


    def draw(self, canvas):
        Polygon.draw(self, canvas)
        TextBox.draw(self, canvas)

    def to_dict(self):
        # print("运行了一次")
        data = {
            "type": self.__class__.__name__,
            "is_filled": self.is_filled,
            "color": self.color,
            "line_width": self.line_width,
            "fill": self.fill,
            "auto_fill": self.auto_fill,
            "version": "2.0",  # 新增版本标识
            "points": [[round(x, 4), round(y, 4)] for x, y in self.points],  # 精度优化
            "closed": True,  # 标识多边形闭合特性
            "transform":[round(v, 6) for v in self.transform_matrix],
            "text": self.text,
            "x": self.x,
            "y": self.y,
            "font_size": self.font_size,
            "font_family": self.font_family,
        }
        print(f"data = {data}")
        return data
    @classmethod
    def from_dict(cls, data):
        # version = data.get("version", "1.0")
        return cls(
            points=data["points"],
            is_filled=data.get("is_filled", data.get("auto_fill", False)),  # 兼容旧版字段
            color=data["color"],
            line_width=data["line_width"],
            transform=data.get("transform", [1, 0, 0, 1, 0, 0]),  # 默认单位矩阵
            fill=data.get("fill", data["color"]),  # 填充色回退逻辑
            auto_fill=data.get("auto_fill", False),
            x=data["x"],
            y=data["y"],
            text=data["text"],
            font_size=data["font_size"],
            font_family=data["font_family"],
        )

class OutputOrInput(Polygon,TextBox):
    def __init__(self, points, x, y, font_size, color, line_width, **kwargs):
        # print(f"OutputOrInput.__mro__:{OutputOrInput.__mro__}")
        kwargs.update({
            "points": points,
            "polygon_type": "parallelogram",
            "x": x,
            "y": y,
            "font_size": font_size,
            "color": color,
            "line_width": line_width
        })
        super().__init__(**kwargs)  # 通过MRO自动分配参数

        # 预期输出：Decision → Polygon → TextBox → Shape → object

    def draw(self, canvas):
        Polygon.draw(self, canvas)
        TextBox.draw(self, canvas)


    def to_dict(self):
        # print("运行了一次")
        data = {
            "type": self.__class__.__name__,
            "is_filled": self.is_filled,
            "color": self.color,
            "line_width": self.line_width,
            "fill": self.fill,
            "auto_fill": self.auto_fill,
            "version": "2.0",  # 新增版本标识
            "points": [[round(x, 4), round(y, 4)] for x, y in self.points],  # 精度优化
            "closed": True,  # 标识多边形闭合特性
            "transform":[round(v, 6) for v in self.transform_matrix],
            "text": self.text,
            "x": self.x,
            "y": self.y,
            "font_size": self.font_size,
            "font_family": self.font_family,
        }
        print(f"data = {data}")
        return data
    @classmethod
    def from_dict(cls, data):
        # version = data.get("version", "1.0")
        return cls(
            points=data["points"],
            is_filled=data.get("is_filled", data.get("auto_fill", False)),  # 兼容旧版字段
            color=data["color"],
            line_width=data["line_width"],
            transform=data.get("transform", [1, 0, 0, 1, 0, 0]),  # 默认单位矩阵
            fill=data.get("fill", data["color"]),  # 填充色回退逻辑
            auto_fill=data.get("auto_fill", False),
            x=data["x"],
            y=data["y"],
            text=data["text"],
            font_size=data["font_size"],
            font_family=data["font_family"],
        )


import math
from typing import List, Tuple


class FlowLine(Line):
    def __init__(self, bend_points=None, canvas_objects=None,is_file_mode=None, ** kwargs):
        """
        :param canvas_objects: 画布上所有图形对象的列表（用于碰撞检测）
        """
        super().__init__(**kwargs)
        # print(f"start:{self.start}")
        if canvas_objects is None:
            canvas_objects = []
        if bend_points is None:
            bend_points = []
        self.is_file_mode = is_file_mode
        self.arrow_size = 10
        self.arrow_angle = 30
        self.canvas_objects = canvas_objects
        self.bend_points = bend_points  # 存储路径转折点坐标

    def calculate_path(self):
        """核心路径规划算法"""
        start = self._apply_transform(self.start)
        end = self._apply_transform(self.end)

        # 检测直接连接是否可行（网页11碰撞检测逻辑）
        if not self._has_collision(start, end):
            self.bend_points = []
            return

        # 生成直角路径（网页5/网页11直角算法）
        dx = end[0] - start[0]
        dy = end[1] - start[1]

        # 优先水平-垂直连接
        bend1 = (start[0] + dx // 2, start[1])
        bend2 = (bend1[0], end[1])
        if not self._has_collision(start, bend1) and \
                not self._has_collision(bend1, bend2) and \
                not self._has_collision(bend2, end):
            self.bend_points = [bend1, bend2]
            return

        # 垂直-水平备用方案
        bend1 = (start[0], start[1] + dy // 2)
        bend2 = (end[0], bend1[1])
        self.bend_points = [bend1, bend2]

    def _has_collision(self, p1: Tuple[int, int], p2: Tuple[int, int]) -> bool:
        """线段碰撞检测"""
        line_bbox = (
            min(p1[0], p2[0]) - self.line_width,
            min(p1[1], p2[1]) - self.line_width,
            max(p1[0], p2[0]) + self.line_width,
            max(p1[1], p2[1]) + self.line_width
        )

        for obj in self.canvas_objects:
            if obj == self: continue
            obj_bbox = obj.get_bounding_box()
            if self._bbox_overlap(line_bbox, obj_bbox):
                return True
        return False

    @staticmethod
    def _bbox_overlap(rect1: Tuple, rect2: Tuple) -> bool:
        """边界框碰撞检测"""
        return not (rect1[2] < rect2[0] or
                    rect1[0] > rect2[2] or
                    rect1[3] < rect2[1] or
                    rect1[1] > rect2[3])


    def draw(self, canvas):
        original_start = self.start
        original_end = self.end
        if not self.is_file_mode:
            self.calculate_path()

        points = [original_start] + self.bend_points + [original_end]

        # print(f"points:{points}")
        # 使用临时变量绘制线段（网页5避免状态污染）
        for i in range(len(points) - 1):
            # 绘制每段连接线（复用父类逻辑）
            self.start = points[i]
            self.end = points[i + 1]
            super().draw(canvas)

        # 恢复原始坐标,仅在末端绘制箭头
        self.start = points[-2]
        self.end = points[-1]
        # 获取变换后坐标
        p0 = self._apply_transform(self.start)
        p1 = self._apply_transform(self.end)

        # 关键修复点1：正确计算方向向量
        dx = p1[0] - p0[0]
        dy = p1[1] - p0[1]
        angle = math.atan2(dy, dx)

        # 关键修复点2：调整顶点生成方向
        arrow_length = self.arrow_size * self.transform_matrix[0]
        left_point = (
            int(round(p1[0] + arrow_length * math.cos(angle + self.arrow_angle / 2))),
            int(round(p1[1] + arrow_length * math.sin(angle + self.arrow_angle / 2)))
        )
        right_point = (
            int(round(p1[0] + arrow_length * math.cos(angle - self.arrow_angle / 2))),
            int(round(p1[1] + arrow_length * math.sin(angle - self.arrow_angle / 2)))
        )

        # 绘制箭头
        canvas.create_polygon(
            p1[0], p1[1],
            left_point[0], left_point[1],
            right_point[0], right_point[1],
            fill=self.color,
            outline=self.color,
            width=self.line_width
        )
        self.start = original_start
        self.end = original_end


    def to_dict(self):
        data = super().to_dict()
        data.update({
            "start":self.start,
            "end":self.end,
            "bend_points":self.bend_points,
            "is_file_mode":True,
            "canvas_objects":[]
        })
        print(f"data = \n{data}\n")
        return data

    @classmethod
    def from_dict(cls, data):
        return cls(
            start_x=data["start"][0],
            start_y=data["start"][1],
            end_x=data["end"][0],
            end_y=data["end"][1],
            color=data["color"],
            line_width=data["line_width"],
            transform=data["transform"],
            fill=data.get("fill"),
            auto_fill=data.get("auto_fill", False),
            bend_points=data.get("bend_points"),
            is_file_mode=data.get("is_file_mode"),
            canvas_objects=data.get("canvas_objects")
        )





